﻿using System;
using System.Collections.Generic;
using System.Text;
using BitmapImage;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace Puri_on_Capture.MPEG
{
    class myMpeg
    {
        // Function that converts the image in pictureBox1 to an
        // MPEG format image.  The number of frames to write to the
        // output file is specified by nPictures.

        
        private long outBytes = 0;
        BinaryWriter bw;

        
        public myMpeg(String fileName)
        {
            // Create output file and Memory Stream to write encoded image to
            bw = new BinaryWriter(File.Create(fileName));
            
            FileInfo info = new FileInfo(fileName);

        }
        public void close()
        {
            // Write the End Of Sequence header
            bw.Write((byte)0x00);
            bw.Write((byte)0x00);
            bw.Write((byte)0x01);
            bw.Write((byte)0xb7);
            bw.Close();
        }

        public void addFrame(Bitmap img,int frame)
        {
            //MessageBox.Show("asdf");
            
            int i;
            int j, k1, k2;
            long j2;
            byte tempByte;

            int ACSIZE = 1764;
            byte[] leftoverBits = new byte[10];
            byte[] DCbits = new byte[24];
            byte[] ACbits = new byte[ACSIZE];

            int DCY, DCCR, DCCB, lastDCY, lastDCCR, lastDCCB;
            int hblock, vblock;
            byte[,] Y = new byte[16, 16];
            byte[,] CR = new byte[8, 8];
            byte[,] CB = new byte[8, 8];
            byte[,] block = new byte[8, 8];
            double[,] S = new double[8, 8];
            int[,] Q = new int[8, 8];
            int[] ZZ = new int[64];
            long imageBytes = 0;
            long compressedBytes = 0;
            double compressPercent = 0.0;
            
            long bitRate;

            MPEGFunctions MPEG = new MPEGFunctions();
            MemoryStream ms = new MemoryStream();

            
            imageBytes = img.Height * img.Width * 3;

            //	Set up variables to encode image into MPEG frame
            lastDCY = 128;
            lastDCCR = lastDCCB = 128;
            for (i = 0; i < 10; i++)
                leftoverBits[i] = 255;
            for (i = 0; i < 24; i++)
                ACbits[i] = 255;
            for (i = 0; i < 24; i++)
                DCbits[i] = 255;

            outBytes = 20;

            
            // Write MPEG picture and slice headers to MemoryStream
            for (i = 0; i < 10; i++)
                MPEG.picHeaderBits[i + 32] = (byte)((0 & (int)Math.Pow(2, 9 - i)) >> (9 - i));
            MPEG.writeToMS(leftoverBits, MPEG.picHeaderBits, ACbits, ref outBytes);
            MPEG.writeToMS(leftoverBits, MPEG.sliceHeaderBits, ACbits, ref outBytes);

            // Do this for each 16x16 pixel block in the bitmap file
            for (vblock = 0; vblock < img.Height / 16; vblock++)
                for (hblock = 0; hblock < img.Width / 16; hblock++)
                {
                    //	Write 2 bits for Macroblock header to leftoverbits
                    //	leftoverbits = '1', '1';
                    MPEG.writeMbHeader(leftoverBits);

                    //	Fill the Y[] array with a 16x16 block of RGB values
                    Y = MPEG.getYMatrix(img, vblock, hblock);
                    //	Fill the CR and CB arrays with 8x8 blocks by subsampling 
                    //	the RGB array
                    CR = MPEG.getCRMatrix(img, vblock, hblock);
                    CB = MPEG.getCBMatrix(img, vblock, hblock);

                    // First calculate DCTs for the 4 Y blocks
                    for (k1 = 0; k1 < 2; k1++)
                        for (k2 = 0; k2 < 2; k2++)
                        {
                            //	Put 8x8 Y blocks into the block[] array and
                            //	then calculate the DCT and quantize the result
                            for (i = 0; i < 8; i++)
                                for (j = 0; j < 8; j++)
                                    block[i, j] = Y[(k1 * 8 + i), (k2 * 8 + j)];
                            S = MPEG.calculateDCT(block);
                            Q = MPEG.Quantize(S);

                            //	Section to differentially Huffman encode DC values
                            //	DC is the diffential value for the DC coefficient
                            //	lastDC is the running total of the full magnitude
                            //	Then send the DC value to DCHuffmanEncode
                            for (i = 0; i < 24; i++)
                                DCbits[i] = 255;
                            DCY = Q[0, 0] - lastDCY;
                            lastDCY += DCY;
                            DCbits = MPEG.DCHuffmanEncode(DCY, MPEG.DCLumCode, MPEG.DCLumSize);

                            //	Section to encode AC Huffman values
                            //	Put the AC coefficients into the ACarray[]
                            //	in zigzag order, then Huffman encode the
                            //	resulting array.
                            for (i = 0; i < ACSIZE; i++)
                                ACbits[i] = 255;
                            ZZ = MPEG.Zigzag(Q);
                            ACbits = MPEG.ACHuffmanEncode(ZZ);

                            //	Write the encoded bits to the MemoryStream
                            MPEG.writeToMS(leftoverBits, DCbits, ACbits, ref outBytes);
                        }

                    // Now calculate the DCT for the CB array and quantize
                    S = MPEG.calculateDCT(CB);
                    Q = MPEG.Quantize(S);

                    //	Encode DC value
                    for (i = 0; i < 24; i++)
                        DCbits[i] = 255;
                    DCCB = Q[0, 0] - lastDCCB;
                    lastDCCB += DCCB;
                    DCbits = MPEG.DCHuffmanEncode(DCCB, MPEG.DCChromCode, MPEG.DCChromSize);

                    //	Encode AC values
                    for (i = 0; i < ACSIZE; i++)
                        ACbits[i] = 255;
                    ZZ = MPEG.Zigzag(Q);
                    ACbits = MPEG.ACHuffmanEncode(ZZ);

                    //	Write the encoded bits to the MemoryStream
                    MPEG.writeToMS(leftoverBits, DCbits, ACbits, ref outBytes);

                    // Now calculate the DCT for the CR array and quantize
                    S = MPEG.calculateDCT(CR);
                    Q = MPEG.Quantize(S);

                    // Encode DC value
                    for (i = 0; i < 24; i++)
                        DCbits[i] = 255;
                    DCCR = Q[0, 0] - lastDCCR;
                    lastDCCR += DCCR;
                    DCbits = MPEG.DCHuffmanEncode(DCCR, MPEG.DCChromCode, MPEG.DCChromSize);

                    //	Encode AC values
                    for (i = 0; i < ACSIZE; i++)
                        ACbits[i] = 255;
                    ZZ = MPEG.Zigzag(Q);
                    ACbits = MPEG.ACHuffmanEncode(ZZ);

                    //	Write the encoded bits to the MemoryStream
                    MPEG.writeToMS(leftoverBits, DCbits, ACbits, ref outBytes);
                }

            // Write EOP bits to the MemoryStream
            MPEG.writeEOP(leftoverBits, MPEG.EOPBits);
            outBytes++;

            //	Put memory stream (which contains the encoded image) into buffer
            ms = MPEG.getMS();
            byte[] buffer = new Byte[ms.Length];
            buffer = ms.ToArray();

            //	Set MPEG Sequence Header bits to correct image size
            j = 2048;
            for (i = 0; i < 12; i++)
            {
                MPEG.seqHeaderBits[i + 32] = (byte)((j & img.Width) >> (11 - i));
                MPEG.seqHeaderBits[i + 44] = (byte)((j & img.Height) >> (11 - i));
                j >>= 1;
            }

            //	Set MPEG Sequence Header bits to bitRate value
            bitRate = ms.Length * 30 * 8 / 400;
            j2 = 131072;
            for (i = 0; i < 18; i++)
            {
                MPEG.seqHeaderBits[i + 64] = (byte)((j2 & bitRate) >> (17 - i));
                j2 >>= 1;
            }

            //	Write MPEG Sequence header to file
            for (i = 0; i < 12; i++)
            {
                tempByte = 0;
                for (j = 0; j < 8; j++)
                    tempByte = (byte)(tempByte * 2 + MPEG.seqHeaderBits[i * 8 + j]);
                bw.Write(tempByte);
            }

            //	Write MPEG GOP header to file
            for (i = 0; i < 8; i++)
            {
                tempByte = 0;
                for (j = 0; j < 8; j++)
                    tempByte = (byte)(tempByte * 2 + MPEG.GOPHeaderBits[i * 8 + j]);
                bw.Write(tempByte);
            }

            //	Fix the picture header for each MPEG frame and write 
            //	the buffer to the file

            for (i = 0; i < frame; i++)
            {
                for (j = 0; j < 10; j++)
                    MPEG.picHeaderBits[j + 32] = (byte)((i & (int)Math.Pow(2, 9 - j)) >> (9 - j));
                for (j = 0; j < 4; j++)
                {
                    tempByte = 0;
                    for (k1 = 0; k1 < 8; k1++)
                        tempByte = (byte)(2 * tempByte + MPEG.picHeaderBits[j * 8 + k1]);
                    buffer[j] = tempByte;
                }
                bw.Write(buffer);
            }
    
            

            // Calculate some output statistics
            outBytes += 4;
            compressedBytes = outBytes - 20 - 8 - 4;
            compressPercent = 100.0 - (double)(compressedBytes * 100.0 / imageBytes);

            

        }
    }
}
